//
// Copyright 2020 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// stylelint-disable selector-class-pattern --
// NOTE: We disable `selector-class-pattern` above to allow using `mdc-` class
// selectors.

@use 'sass:map';
@use 'sass:math';
@use 'sass:list';
@use 'sass:meta';
@use '@material/animation/functions';
@use '@material/checkbox/checkbox-theme';
@use '@material/density/density';
@use '@material/elevation/mixins';
@use '@material/feature-targeting/feature-targeting';
@use '@material/icon-button/icon-button-theme';
@use '@material/list/evolution-mixins' as list-theme;
@use '@material/rtl/rtl';
@use '@material/select/select-theme';
@use '@material/shape/functions' as shape-functions;
@use '@material/shape/mixins' as shape-mixins;
@use '@material/theme/keys';
@use '@material/theme/theme';
@use '@material/theme/theme-color';
@use '@material/tokens/v0_132' as tokens;
@use '@material/touch-target/touch-target';
@use '@material/typography/typography';

$fill-color: surface !default;
$header-row-fill-color: surface !default;
$row-fill-color: inherit !default;
$selected-row-fill-color: rgba(theme-color.prop-value(primary), 0.04) !default;
$checked-icon-color: primary !default;
$divider-color: rgba(theme-color.prop-value(on-surface), 0.12) !default;
$divider-size: 1px !default;
$row-hover-fill-color: rgba(theme-color.prop-value(on-surface), 0.04) !default;

$header-row-text-color: rgba(theme-color.prop-value(on-surface), 0.87) !default;
$row-text-color: rgba(theme-color.prop-value(on-surface), 0.87) !default;

$sort-icon-color: rgba(theme-color.prop-value(on-surface), 0.6) !default;
$sort-icon-active-color: rgba(
  theme-color.prop-value(on-surface),
  0.87
) !default;
$sort-icon-density-scale: -5 !default;

$shape-radius: medium !default;
$stroke-size: 1px !default;
$stroke-color: rgba(theme-color.prop-value(on-surface), 0.12) !default;

$row-height: 52px !default;
$header-row-height: get-header-row-height($row-height) !default;
$cell-leading-padding: 16px !default;
$cell-trailing-padding: 16px !default;

$minimum-row-height: 36px !default;
$maximum-row-height: $row-height !default;
$default-density-scale: density.$default-scale !default;
$density-config: (
  height: (
    maximum: $row-height,
    default: $row-height,
    minimum: $minimum-row-height,
  ),
);
$pagination-rows-per-page-select-height: 36px;

$light-theme: tokens.md-comp-data-table-values();

@function remove-unsupported-keys($theme) {
  $theme: map.remove(
    $theme,
    (
      // TODO(b/254356584): We don't set pagination background-color.
      'footer-container-color',
      // TODO(b/254356584): Select doesn't have Theming API support yet.
      'footer-outlined-select-text-field-container-height',
      // TODO(b/254356584): We don't add hover styles on header.
      'header-hover-headline-color',
      // TODO(b/254356584): We don't add hover styles on header.
      'header-hover-sorting-icon-button-color',
      // TODO(b/254356584): We don't support disabled rows.
      'row-item-disabled-label-text-color',
      // TODO(b/254356584): We don't support disabled rows.
      'row-item-disabled-label-text-opacity',
      // TODO(b/254356584): We don't add hover styles on selected rows.
      'row-item-selected-hover-state-layer-color',
      // TODO(b/254356584): We don't add hover styles on selected rows.
      'row-item-selected-hover-state-layer-opacity',
      // TODO(b/254356584): We don't have state layers on rows.
      'row-item-unselected-hover-state-layer-color'
        // TODO(b/254356584): We don't have state layers on rows.
      'row-item-unselected-hover-state-layer-opacity'
    )
  );
  @return $theme;
}

$_validation-theme: remove-unsupported-keys($light-theme);

$custom-property-prefix: 'data-table';

@mixin theme($theme) {
  @include theme.validate-theme($_validation-theme, $theme);
  @include keys.declare-custom-properties($theme, $custom-property-prefix);
}

@mixin theme-styles($theme, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);
  $feat-structure: feature-targeting.create-target($query, structure);
  $feat-typography: feature-targeting.create-target($query, typography);

  @include theme.validate-theme-styles($_validation-theme, $theme);
  $theme: keys.create-theme-properties($theme, $custom-property-prefix);

  ///////////
  // Table //
  ///////////

  @include shape-radius(map.get($theme, 'container-shape'), $query: $query);
  @include stroke-color(map.get($theme, 'outline-color'), $query: $query);
  @include stroke-size(map.get($theme, 'outline-width'), $query: $query);

  ////////////
  // Header //
  ////////////

  @include header-row-fill-color(
    map.get($theme, 'header-container-color'),
    $query: $query
  );
  @include header-row-height(
    map.get($theme, 'header-container-height'),
    $query: $query
  );
  @include header-row-text-color(
    map.get($theme, 'header-headline-color'),
    $query: $query
  );
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-typography) {
      $font: map.get($theme, 'header-headline-font');
      $line-height: map.get($theme, 'header-headline-line-height');
      $size: map.get($theme, 'header-headline-size');
      $tracking: map.get($theme, 'header-headline-tracking');
      $weight: map.get($theme, 'header-headline-weight');

      @include theme.property('font-family', $font);
      @include theme.property('line-height', $line-height);
      @include theme.property('font-size', $size);
      @include theme.property('letter-spacing', $tracking);
      @include theme.property('font-weight', $weight);
    }
  }

  //////////
  // Rows //
  //////////

  // Separated because row-height() sets min-height on pagination
  .mdc-data-table__row {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(
        'height',
        map.get($theme, 'row-item-container-height')
      );
    }
  }
  .mdc-data-table__cell {
    @include feature-targeting.targets($feat-typography) {
      $font: map.get($theme, 'row-item-label-text-font');
      $line-height: map.get($theme, 'row-item-label-text-line-height');
      $size: map.get($theme, 'row-item-label-text-size');
      $tracking: map.get($theme, 'row-item-label-text-tracking');
      $weight: map.get($theme, 'row-item-label-text-weight');

      @include theme.property('font-family', $font);
      @include theme.property('line-height', $line-height);
      @include theme.property('font-size', $size);
      @include theme.property('letter-spacing', $tracking);
      @include theme.property('font-weight', $weight);
    }

    @include feature-targeting.targets($feat-color) {
      // Separated because row-text-color() also sets color on pagination text
      @include theme.property(
        'color',
        map.get($theme, 'row-item-label-text-color')
      );
    }
  }
  .mdc-data-table__cell,
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-color) {
      // Separated because divider-color() also sets color on pagination select
      @include theme.property(
        'border-bottom-color',
        map.get($theme, 'row-item-outline-color')
      );
    }
  }
  .mdc-data-table__pagination {
    @include feature-targeting.targets($feat-color) {
      // Separated because divider-color() also sets color on pagination select
      @include theme.property(
        'border-top-color',
        map.get($theme, 'row-item-outline-color')
      );
    }
  }
  @include divider-size(
    map.get($theme, 'row-item-outline-width'),
    $query: $query
  );
  @include row-fill-color(
    map.get($theme, 'row-item-unselected-container-color'),
    $query: $query
  );
  @include selected-row-fill-color(
    map.get($theme, 'row-item-selected-container-color'),
    $query: $query
  );

  ////////////
  // Footer //
  ////////////

  .mdc-data-table__pagination {
    @include feature-targeting.targets($feat-typography) {
      $font: map.get($theme, 'footer-supporting-text-font');
      $line-height: map.get($theme, 'footer-supporting-text-line-height');
      $size: map.get($theme, 'footer-supporting-text-size');
      $tracking: map.get($theme, 'footer-supporting-text-tracking');
      $weight: map.get($theme, 'footer-supporting-text-weight');

      @include theme.property('font-family', $font);
      @include theme.property('line-height', $line-height);
      @include theme.property('font-size', $size);
      @include theme.property('letter-spacing', $tracking);
      @include theme.property('font-weight', $weight);
    }

    @include feature-targeting.targets($feat-structure) {
      // Separated because row-height() also sets height on row
      @include theme.property(
        'min-height',
        map.get($theme, 'footer-container-height')
      );
    }
  }
  .mdc-data-table__pagination-total,
  .mdc-data-table__pagination-rows-per-page-label {
    @include feature-targeting.targets($feat-color) {
      // Separated because row-text-color() also sets color on row text
      @include theme.property(
        'color',
        map.get($theme, 'footer-supporting-text-color')
      );
    }
  }
}

@function get-header-row-height($height) {
  @return $height + 4px;
}

/// Sets the color of sort icon button when it is in idle state.
/// (icon showed on header cell focus)
/// @param {String} $color - Color of sort icon button
@mixin sort-icon-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__sort-icon-button {
    @include icon-button-theme.ink-color($color, $query: $query);
  }
}

/// Sets the color of sort icon button when it is activated (sorted).
/// @param {String} $color - Color of sort icon button
@mixin sort-icon-active-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__header-cell--sorted .mdc-data-table__sort-icon-button {
    @include icon-button-theme.ink-color($color, $query: $query);
  }
}

@mixin fill-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  @include feature-targeting.targets($feat-color) {
    @include theme.property('background-color', $color);
  }
}

@mixin header-row-fill-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  // Set background color to cell instead of row to support sticky header.
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('background-color', $color);
    }
  }
}

@mixin row-fill-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__row {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('background-color', $color);
    }
  }
}

@mixin selected-row-fill-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__row--selected {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('background-color', $color);
    }
  }
}

@mixin checked-icon-color($color, $query: feature-targeting.all()) {
  .mdc-data-table__header-row-checkbox,
  .mdc-data-table__row-checkbox {
    @include checkbox-theme.focus-indicator-color($color, $query: $query);
    @include checkbox-theme.container-colors(
      $marked-stroke-color: $color,
      $marked-fill-color: $color,
      $query: $query
    );
  }
}

///
/// Sets divider color of data table (including outline color of rows per page
/// select). Use `stroke-color()` to set table border color.
/// @param {Color} $color Divider color.
///
@mixin divider-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__pagination-rows-per-page-select--outlined {
    @include select-theme.outline-color($color, $query: $query);
  }

  .mdc-data-table__cell,
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('border-bottom-color', $color);
    }
  }

  .mdc-data-table__pagination {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('border-top-color', $color);
    }
  }
}

@mixin divider-size($size, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__cell,
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property('border-bottom-width', $size);
      border-bottom-style: solid;
    }
  }

  .mdc-data-table__pagination {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property('border-top-width', $size);
      border-top-style: solid;
    }
  }

  .mdc-data-table__row:last-child > .mdc-data-table__cell {
    @include feature-targeting.targets($feat-structure) {
      border-bottom: none;
    }
  }
}

@mixin row-hover-fill-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('background-color', $color);
    }
  }
}

@mixin header-row-text-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('color', $color);
    }
  }
}

///
/// Sets row text color (including pagination row text).
/// @param {Color} $color Row text color
///
@mixin row-text-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  .mdc-data-table__pagination-total,
  .mdc-data-table__pagination-rows-per-page-label,
  .mdc-data-table__cell {
    @include feature-targeting.targets($feat-color) {
      @include theme.property('color', $color);
    }
  }
}

///
/// Sets rounded shape radius to data table.
/// @param {Number|List} $radius - Shape radius in `border-radius` CSS format.
/// @param {Boolean} $rtl-reflexive - Set to `true` to flip radius corners in
///     RTL context.
///
@mixin shape-radius(
  $radius,
  $rtl-reflexive: false,
  $query: feature-targeting.all()
) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query);

  // Apply same border radius as parent to leading/trailing header cells,
  // and leading/trailing cells of last row.
  // Cells that have explicit background color applied require border
  // radius to take the parents' rounded shape.
  $border-radius: shape-functions.unpack-radius($radius);
  $top-left-radius: shape-functions.resolve-radius(list.nth($border-radius, 1));
  $top-right-radius: shape-functions.resolve-radius(
    list.nth($border-radius, 2)
  );
  $bottom-right-radius: shape-functions.resolve-radius(
    list.nth($border-radius, 3)
  );
  $bottom-left-radius: shape-functions.resolve-radius(
    list.nth($border-radius, 4)
  );

  .mdc-data-table__header-cell:first-child {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(border-top-left-radius, $top-left-radius);

      @include rtl.rtl {
        @include theme.property(
          border-top-right-radius,
          if($rtl-reflexive, $top-left-radius, $top-right-radius)
        );

        border-top-left-radius: 0;
      }
    }
  }

  .mdc-data-table__header-cell:last-child {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(border-top-right-radius, $top-right-radius);

      @include rtl.rtl {
        @include theme.property(
          border-top-left-radius,
          if($rtl-reflexive, $top-right-radius, $top-left-radius)
        );

        border-top-right-radius: 0;
      }
    }
  }

  &.mdc-data-table--without-footer
    .mdc-data-table__row:last-child
    > .mdc-data-table__cell:first-child {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(border-bottom-left-radius, $bottom-left-radius);

      @include rtl.rtl {
        @include theme.property(
          border-bottom-right-radius,
          if($rtl-reflexive, $bottom-left-radius, $bottom-right-radius)
        );

        border-bottom-left-radius: 0;
      }
    }
  }

  &.mdc-data-table--without-footer
    .mdc-data-table__row:last-child
    > .mdc-data-table__cell:last-child {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property(border-bottom-right-radius, $bottom-right-radius);

      @include rtl.rtl {
        @include theme.property(
          border-bottom-left-radius,
          if($rtl-reflexive, $bottom-right-radius, $bottom-left-radius)
        );

        border-bottom-right-radius: 0;
      }
    }
  }
}

@mixin stroke-size($size, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @include feature-targeting.targets($feat-structure) {
    @include theme.property('border-width', $size);
    border-style: solid;
  }
}

@mixin stroke-color($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  @include feature-targeting.targets($feat-color) {
    @include theme.property('border-color', $color);
  }
}

@mixin header-row-height($height, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__header-row {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property('height', $height);
    }
  }
}

@mixin row-height($height, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__row {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property('height', $height);
    }
  }

  .mdc-data-table__pagination {
    @include feature-targeting.targets($feat-structure) {
      @include theme.property('min-height', $height);
    }
  }
}

///
/// Sets cell padding including cell, header cell, row checkbox cell and header
/// row checkbox cell.
/// @param {Number} $leading-padding [$cell-leading-padding] Leading padding.
/// @param {Number} $trailing-padding [$cell-trailing-padding] Trailing padding.
/// @param {Number} $checkbox-touch-size [$checkbox-touch-size] Checkbox Touch
///     Size. Use this to adjust row checkbox cell leading padding based on
///     checkbox density scale.
/// @param {Number} $row-checkbox-density-scale [null] Density scale of row
///     checkbox. Use this to adjust alignment of row checkbox within a cell.
///     Ignore if data table's density scale is 0.
///     See `checkbox-theme.density()` mixin for supported density scales.
///
@mixin cell-padding(
  $leading-padding: $cell-leading-padding,
  $trailing-padding: $cell-trailing-padding,
  $row-checkbox-density-scale: null,
  $query: feature-targeting.all()
) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__cell,
  .mdc-data-table__header-cell {
    @include feature-targeting.targets($feat-structure) {
      padding: 0 $trailing-padding 0 $leading-padding;
    }
  }

  @include checkbox-cell-padding(
    $leading-padding: $leading-padding,
    $row-checkbox-density-scale: $row-checkbox-density-scale,
    $query: $query
  );
}

///
/// Sets only row checkbox cell and header row checkbox cell leading padding.
/// Use `cell-padding()` to set all cell's padding.
/// @param {Number} $leading-padding [$cell-leading-padding] Leading padding.
/// @param {Number} $checkbox-touch-size [$checkbox-touch-size] Checkbox Touch
///     Size. Use this to adjust row checkbox cell leading padding based on
///     checkbox density scale.
/// @param {Number} $row-checkbox-density-scale [null] Density scale of row
///     checkbox. Use this to adjust alignment of row checkbox within a cell.
///     Ignore if data table's density scale is 0.
///     See `checkbox-theme.density()` mixin for supported density scales.
///
@mixin checkbox-cell-padding(
  $leading-padding: $cell-leading-padding,
  $row-checkbox-density-scale: null,
  $query: feature-targeting.all()
) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__header-cell--checkbox,
  .mdc-data-table__cell--checkbox {
    @include feature-targeting.targets($feat-structure) {
      // Distance from leading cell bound to checkbox's icon bound should be
      // 16dp (`$leading-padding`). Calculate required padding excluding
      // checkbox bounds.
      $checkbox-icon-size: 24px;
      $checkbox-touch-size: touch-target.$height;
      @if $row-checkbox-density-scale and $row-checkbox-density-scale < 0 {
        $checkbox-touch-size: checkbox-theme.get-ripple-size(
          $row-checkbox-density-scale
        );
      }
      $leading-padding: $leading-padding -
        math.div($checkbox-touch-size - $checkbox-icon-size, 2);
      @include rtl.reflexive-property(padding, $leading-padding, 0);
    }
  }
}

@mixin column-widths($width-list, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @for $i from 1 through list.length($width-list) {
    .mdc-data-table__row > :nth-child(#{$i}) {
      @include feature-targeting.targets($feat-structure) {
        width: list.nth($width-list, $i);
      }
    }
  }
}

///
/// Sets density scale for data table. Use corresponding density mixins of child components (such as Checkbox) to apply
/// density scales which will be rendered inside data table.
///
/// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`,
///     `-3`, `-2`, `-1`, `0`.
/// @param {Number} $row-checkbox-density-scale [null] Density scale of row
///     checkbox. Use this to set density of row checkbox and also
///     automatically adjust the alignment of row checkbox within a cell.
///     See `checkbox.density()` mixin for supported density scales.
///
@mixin density(
  $density-scale,
  $row-checkbox-density-scale: null,
  $pagination-select-density-scale: null,
  $query: feature-targeting.all()
) {
  $height: density.prop-value(
    $density-config: $density-config,
    $density-scale: $density-scale,
    $property-name: height,
  );

  @include row-height($height, $query: $query);
  @include header-row-height(get-header-row-height($height), $query: $query);

  @if $row-checkbox-density-scale {
    @include checkbox-cell-padding(
      $leading-padding: $cell-leading-padding,
      $row-checkbox-density-scale: $row-checkbox-density-scale,
      $query: $query
    );

    .mdc-data-table__header-row-checkbox,
    .mdc-data-table__row-checkbox {
      @include checkbox-theme.density($row-checkbox-density-scale);
    }
  }

  @if $pagination-select-density-scale {
    @include select-density($pagination-select-density-scale);
  }
}

@mixin select-density($density-scale) {
  @include select-theme.outlined-height(
    select-theme.$height + density.$interval * $density-scale
  );
  .mdc-list-item {
    @include list-theme.one-line-item-density(
      $density-scale + 2,
      $exclude-variants: true
    );
  }

  margin: 0;
}

///
/// Sets maximum height of data table. Use this to make table vertically
/// scrollable.
/// @param {Number} $height
///
@mixin max-height($height, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-data-table__table-container {
    @include feature-targeting.targets($feat-structure) {
      max-height: $height;
    }
  }
}
